{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "images.ipynb",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mt9dL5dIir8X"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "ufPx7EiCiqgR",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License.\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "deletable": true,
        "editable": true,
        "id": "ucMoYase6URl"
      },
      "source": [
        "# Загрузка изображений"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_Wwu5SXZmEkB"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/load_data/images\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Смотрите на TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/tutorials/load_data/images.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запустите в Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/tutorials/load_data/images.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучайте код на GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/ru/tutorials/load_data/images.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Скачайте ноутбук</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3W5HaXTbpf4H",
        "colab_type": "text"
      },
      "source": [
        "Note: Вся информация в этом разделе переведена с помощью русскоговорящего Tensorflow сообщества на общественных началах. Поскольку этот перевод не является официальным, мы не гарантируем что он на 100% аккуратен и соответствует [официальной документации на английском языке](https://www.tensorflow.org/?hl=en). Если у вас есть предложение как исправить этот перевод, мы будем очень рады увидеть pull request в [tensorflow/docs](https://github.com/tensorflow/docs) репозиторий GitHub. Если вы хотите помочь сделать документацию по Tensorflow лучше (сделать сам перевод или проверить перевод подготовленный кем-то другим), напишите нам на [docs-ru@tensorflow.org list](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ru)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Oxw4WahM7DU9"
      },
      "source": [
        "В этом руководстве приведен простой пример загрузки датасета изображений с использованием `tf.data`.\n",
        "\n",
        "Набор данных, используемый в этом примере, распространяется в виде папок изображений, с одним классом изображений в каждой папке."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "deletable": true,
        "editable": true,
        "id": "hoQQiZDB6URn"
      },
      "source": [
        "## Setup"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "QGXxBuPyKJw1",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "try:\n",
        "  # %tensorflow_version существует только в Colab.\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "  pass\n",
        "import tensorflow as tf"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KT6CcaqgQewg",
        "colab": {}
      },
      "source": [
        "AUTOTUNE = tf.data.experimental.AUTOTUNE"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rxndJHNC8YPM"
      },
      "source": [
        "## Скачайте и проверьте набор данных"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "deletable": true,
        "editable": true,
        "id": "wO0InzL66URu"
      },
      "source": [
        "### Получите изображения\n",
        "\n",
        "Перед тем как начать любое обучение вам нужен набор изображений для обучения нейронной сети новым классам которые вы хотите распознавать. Вы уже создали архив распространяемых по свободной лицензии фото цветов для первоначального использования:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rN-Pc6Zd6awg",
        "colab": {}
      },
      "source": [
        "import pathlib\n",
        "data_root_orig = tf.keras.utils.get_file(origin='https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz',\n",
        "                                         fname='flower_photos', untar=True)\n",
        "data_root = pathlib.Path(data_root_orig)\n",
        "print(data_root)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rFkFK74oO--g"
      },
      "source": [
        "После скачивания 218MB, вам теперь доступна копия изображений цветов:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7onR_lWE7Njj",
        "colab": {}
      },
      "source": [
        "for item in data_root.iterdir():\n",
        "  print(item)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "4yYX3ZRqGOuq",
        "colab": {}
      },
      "source": [
        "import random\n",
        "all_image_paths = list(data_root.glob('*/*'))\n",
        "all_image_paths = [str(path) for path in all_image_paths]\n",
        "random.shuffle(all_image_paths)\n",
        "\n",
        "image_count = len(all_image_paths)\n",
        "image_count"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "t_BbYnLjbltQ",
        "colab": {}
      },
      "source": [
        "all_image_paths[:10]"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vkM-IpB-6URx"
      },
      "source": [
        "### Просмотрите изображения\n",
        "Сейчас давайте быстро просмотрим парочку изображений, чтобы вы знали с чем имеете дело:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "wNGateQJ6UR1",
        "colab": {}
      },
      "source": [
        "import os\n",
        "attributions = (data_root/\"LICENSE.txt\").open(encoding='utf-8').readlines()[4:]\n",
        "attributions = [line.split(' CC-BY') for line in attributions]\n",
        "attributions = dict(attributions)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "jgowG2xu88Io",
        "colab": {}
      },
      "source": [
        "import IPython.display as display\n",
        "\n",
        "def caption_image(image_path):\n",
        "    image_rel = pathlib.Path(image_path).relative_to(data_root)\n",
        "    return \"Image (CC BY 2.0) \" + ' - '.join(attributions[str(image_rel)].split(' - ')[:-1])\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "YIjLi-nX0txI",
        "colab": {}
      },
      "source": [
        "for n in range(3):\n",
        "  image_path = random.choice(all_image_paths)\n",
        "  display.display(display.Image(image_path))\n",
        "  print(caption_image(image_path))\n",
        "  print()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "OaNOr-co3WKk"
      },
      "source": [
        "### Определите метку для каждого изображения"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "-weOQpDw2Jnu"
      },
      "source": [
        "Выведите на экран все доступные метки:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "ssUZ7Qh96UR3",
        "colab": {}
      },
      "source": [
        "label_names = sorted(item.name for item in data_root.glob('*/') if item.is_dir())\n",
        "label_names"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "9l_JEBql2OzS"
      },
      "source": [
        "Присвойте индекс каждой метке:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Y8pCV46CzPlp",
        "colab": {}
      },
      "source": [
        "label_to_index = dict((name, index) for index, name in enumerate(label_names))\n",
        "label_to_index"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VkXsHg162T9F"
      },
      "source": [
        "Создайте список всех файлов и индексов их меток:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "q62i1RBP4Q02",
        "colab": {}
      },
      "source": [
        "all_image_labels = [label_to_index[pathlib.Path(path).parent.name]\n",
        "                    for path in all_image_paths]\n",
        "\n",
        "print(\"First 10 labels indices: \", all_image_labels[:10])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "i5L09icm9iph"
      },
      "source": [
        "### Загрузите и отформатируйте изображения"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "SbqqRUS79ooq"
      },
      "source": [
        "TensorFlow включает все инструменты которые вам могут понадобиться для загрузки и обработки изображений:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "jQZdySHvksOu",
        "colab": {}
      },
      "source": [
        "img_path = all_image_paths[0]\n",
        "img_path"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2t2h2XCcmK1Y"
      },
      "source": [
        "Вот сырые данные:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "LJfkyC_Qkt7A",
        "colab": {}
      },
      "source": [
        "img_raw = tf.io.read_file(img_path)\n",
        "print(repr(img_raw)[:100]+\"...\")"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "opN8AVc8mSbz"
      },
      "source": [
        "Преобразуйте ее в тензор изображения:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Tm0tdrlfk0Bb",
        "colab": {}
      },
      "source": [
        "img_tensor = tf.image.decode_image(img_raw)\n",
        "\n",
        "print(img_tensor.shape)\n",
        "print(img_tensor.dtype)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "3k-Of2Tfmbeq"
      },
      "source": [
        "Измените ее размер для вашей модели:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "XFpz-3_vlJgp",
        "colab": {}
      },
      "source": [
        "img_final = tf.image.resize(img_tensor, [192, 192])\n",
        "img_final = img_final/255.0\n",
        "print(img_final.shape)\n",
        "print(img_final.numpy().min())\n",
        "print(img_final.numpy().max())\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "aCsAa4Psl4AQ"
      },
      "source": [
        "Оберните их в простые функции для будущего использования."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "HmUiZJNU73vA",
        "colab": {}
      },
      "source": [
        "def preprocess_image(image):\n",
        "  image = tf.image.decode_jpeg(image, channels=3)\n",
        "  image = tf.image.resize(image, [192, 192])\n",
        "  image /= 255.0  # normalize to [0,1] range\n",
        "\n",
        "  return image"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "einETrJnO-em",
        "colab": {}
      },
      "source": [
        "def load_and_preprocess_image(path):\n",
        "  image = tf.io.read_file(path)\n",
        "  return preprocess_image(image)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3brWQcdtz78y",
        "colab": {}
      },
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "image_path = all_image_paths[0]\n",
        "label = all_image_labels[0]\n",
        "\n",
        "plt.imshow(load_and_preprocess_image(img_path))\n",
        "plt.grid(False)\n",
        "plt.xlabel(caption_image(img_path))\n",
        "plt.title(label_names[label].title())\n",
        "print()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "n2TCr1TQ8pA3"
      },
      "source": [
        "## Постройте `tf.data.Dataset`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6H9Z5Mq63nSH"
      },
      "source": [
        "### Датасет изображений"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GN-s04s-6Luq"
      },
      "source": [
        "Простейший способ построения `tf.data.Dataset` это использование метода `from_tensor_slices`.\n",
        "\n",
        "Нарезка массива строк, приводит к датасету строк:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6oRPG3Jz3ie_",
        "colab": {}
      },
      "source": [
        "path_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "uML4JeMmIAvO"
      },
      "source": [
        "Параметры `shapes` и `types` описывают содержимое каждого элемента датасета. В этом случае у нас множество скалярных двоичных строк"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mIsNflFbIK34",
        "colab": {}
      },
      "source": [
        "print(path_ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ZjyGcM8OwBJ2"
      },
      "source": [
        "Сейчас создадим новый датасет который загружает и форматирует изображения на лету пройдясь с `preprocess_image` по датасету с путями к файлам."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "D1iba6f4khu-",
        "colab": {}
      },
      "source": [
        "image_ds = path_ds.map(load_and_preprocess_image, num_parallel_calls=AUTOTUNE)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "JLUPs2a-lEEJ",
        "colab": {}
      },
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "plt.figure(figsize=(8,8))\n",
        "for n, image in enumerate(image_ds.take(4)):\n",
        "  plt.subplot(2,2,n+1)\n",
        "  plt.imshow(image)\n",
        "  plt.grid(False)\n",
        "  plt.xticks([])\n",
        "  plt.yticks([])\n",
        "  plt.xlabel(caption_image(all_image_paths[n]))\n",
        "  plt.show()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "P6FNqPbxkbdx"
      },
      "source": [
        "### Датасет пар `(image, label)`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "YgvrWLKG67-x"
      },
      "source": [
        "Используя тот же метод `from_tensor_slices` вы можете собрать датасет меток:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "AgBsAiV06udj",
        "colab": {}
      },
      "source": [
        "label_ds = tf.data.Dataset.from_tensor_slices(tf.cast(all_image_labels, tf.int64))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "HEsk5nN0vyeX",
        "colab": {}
      },
      "source": [
        "for label in label_ds.take(10):\n",
        "  print(label_names[label.numpy()])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jHjgrEeTxyYz"
      },
      "source": [
        "Поскольку датасеты следуют в одном и том же порядке, вы можете просто собрать их вместе при помощи функции zip в набор данных пары `(image, label)`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "AOEWNMdQwsbN",
        "colab": {}
      },
      "source": [
        "image_label_ds = tf.data.Dataset.zip((image_ds, label_ds))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "yA2F09SJLMuM"
      },
      "source": [
        "`Shapes` и `types` нового датасета это кортежи размерностей и типов описывающие каждое поле:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "DuVYNinrLL-N",
        "colab": {}
      },
      "source": [
        "print(image_label_ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2WYMikoPWOQX"
      },
      "source": [
        "Примечание. Если у вас есть такие массивы, как «all_image_labels» и «all_image_paths», альтернативой «tf.data.dataset.Dataset.zip» является срез (slice) пары массивов."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "HOFwZI-2WhzV",
        "colab": {}
      },
      "source": [
        "ds = tf.data.Dataset.from_tensor_slices((all_image_paths, all_image_labels))\n",
        "\n",
        "# Кортежи распаковываются в позиционные аргументы отображаемой функции\n",
        "def load_and_preprocess_from_path_label(path, label):\n",
        "  return load_and_preprocess_image(path), label\n",
        "\n",
        "image_label_ds = ds.map(load_and_preprocess_from_path_label)\n",
        "image_label_ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vYGCgJuR_9Qp"
      },
      "source": [
        "### Базовые способы обучения"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wwZavzgsIytz"
      },
      "source": [
        "Для обучения модели на этом датасете, вам необходимо, чтобы данные:\n",
        "\n",
        "* Были хорошо перемешаны.\n",
        "* Были упакованы.\n",
        "* Повторялись вечно.\n",
        "* Пакеты должны быть доступны как можно скорее.\n",
        "\n",
        "Эти свойства могут быть легко добавлены с помощью `tf.data` api."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "uZmZJx8ePw_5",
        "colab": {}
      },
      "source": [
        "BATCH_SIZE = 32\n",
        "\n",
        "# Установка размера буфера перемешивания, равного набору данных, гарантирует\n",
        "# полное перемешивание данных.\n",
        "ds = image_label_ds.shuffle(buffer_size=image_count)\n",
        "ds = ds.repeat()\n",
        "ds = ds.batch(BATCH_SIZE)\n",
        "# `prefetch` позволяет датасету извлекать пакеты в фоновом режиме, во время обучения модели.\n",
        "ds = ds.prefetch(buffer_size=AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "6JsM-xHiFCuW"
      },
      "source": [
        "Здесь необходимо отметить несколько вещей:\n",
        "\n",
        "1. Важна последовательность действий.\n",
        "\n",
        "  * `.shuffle` после `.repeat` перемешает элементы вне границ эпох (некоторые элементы будут увидены дважды в то время как другие ни разу).\n",
        "  * `.shuffle` после `.batch` перемешает порядок пакетов, но не перемешает элементы внутри пакета.\n",
        "\n",
        "1. Используйте `buffer_size` такого же размера как и датасет для полного перемешивания. Вплоть до размера набора данных, большие значения обеспечивают лучшую рандомизацию, но используют больше памяти.\n",
        "\n",
        "1. Из буфера перемешивания не выбрасываются элементы пока он не заполнится. Поэтому большой размер `buffer_size` может стать причиной задержки при запуске `Dataset`.\n",
        "\n",
        "1. Перемешанный датасет не сообщает об окончании, пока буфер перемешивания полностью не опустеет. `Dataset` перезапускается с помощью` .repeat`, вызывая еще одно ожидание заполнения буфера перемешивания.\n",
        "\n",
        "Последний пункт может быть решен использованием метода `tf.data.Dataset.apply` вместе со слитой функцией `tf.data.experimental.shuffle_and_repeat`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Ocr6PybXNDoO",
        "colab": {}
      },
      "source": [
        "ds = image_label_ds.apply(\n",
        "  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))\n",
        "ds = ds.batch(BATCH_SIZE)\n",
        "ds = ds.prefetch(buffer_size=AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GBBZMSuAmQVL"
      },
      "source": [
        "### Передайте датасет в модель\n",
        "\n",
        "Получите копию MobileNet v2 из `tf.keras.applications`.\n",
        "\n",
        "Она будет использована для простого примера передачи обучения (transfer learning).\n",
        "\n",
        "Установите веса MobileNet необучаемыми:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "KbJrXn9omO_g",
        "colab": {}
      },
      "source": [
        "mobile_net = tf.keras.applications.MobileNetV2(input_shape=(192, 192, 3), include_top=False)\n",
        "mobile_net.trainable=False"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Y7NVWiLF3Vbf"
      },
      "source": [
        "Эта модель предполагает нормализацию входных данных в диапазоне `[-1,1]`:\n",
        "\n",
        "```\n",
        "help(keras_applications.mobilenet_v2.preprocess_input)\n",
        "```\n",
        "\n",
        "<pre>\n",
        "...\n",
        "Эта функция применяет препроцессинг \"Inception\" который преобразует\n",
        "RGB значения из [0, 255] в [-1, 1]\n",
        "...\n",
        "</pre>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "CboYya6LmdQI"
      },
      "source": [
        "Перед передачей входных данных в модель MobilNet, вам нужно конвертировать их из диапазона `[0,1]` в `[-1,1]`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "SNOkHUGv3FYq",
        "colab": {}
      },
      "source": [
        "def change_range(image,label):\n",
        "  return 2*image-1, label\n",
        "\n",
        "keras_ds = ds.map(change_range)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "QDzZ3Nye5Rpv"
      },
      "source": [
        "MobileNet возвращает `6x6` сетку признаков для каждого изображения.\n",
        "\n",
        "Передайте ей пакет изображений чтобы увидеть:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "OzAhGkEK6WuE",
        "colab": {}
      },
      "source": [
        "# Датасету может понадобиться несколько секунд для старта пока заполняется буфер перемешивания.\n",
        "image_batch, label_batch = next(iter(keras_ds))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "LcFdiWpO5WbV",
        "colab": {}
      },
      "source": [
        "feature_map_batch = mobile_net(image_batch)\n",
        "print(feature_map_batch.shape)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "vrbjEvaC5XmU"
      },
      "source": [
        "Постройте модель обернутую вокруг MobileNet и используйте `tf.keras.layers.GlobalAveragePooling2D` для усреднения по этим размерностям пространства перед выходным слоем `tf.keras.layers.Dense`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "X0ooIU9fNjPJ",
        "colab": {}
      },
      "source": [
        "model = tf.keras.Sequential([\n",
        "  mobile_net,\n",
        "  tf.keras.layers.GlobalAveragePooling2D(),\n",
        "  tf.keras.layers.Dense(len(label_names), activation = 'softmax')])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "foQYUJs97V4V"
      },
      "source": [
        "Сейчас он производит выходные данные ожидаемых размеров:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "1nwYxvpj7ZEf",
        "colab": {}
      },
      "source": [
        "logit_batch = model(image_batch).numpy()\n",
        "\n",
        "print(\"min logit:\", logit_batch.min())\n",
        "print(\"max logit:\", logit_batch.max())\n",
        "print()\n",
        "\n",
        "print(\"Shape:\", logit_batch.shape)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "pFc4I_J2nNOJ"
      },
      "source": [
        "Скомпилируйте модель чтобы описать процесс обучения:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "ZWGqLEWYRNvv",
        "colab": {}
      },
      "source": [
        "model.compile(optimizer=tf.keras.optimizers.Adam(),\n",
        "              loss='sparse_categorical_crossentropy',\n",
        "              metrics=[\"accuracy\"])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tF1mO6haBOSd"
      },
      "source": [
        "Есть две переменные для обучения -  Dense `weights` и `bias`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "pPQ5yqyKBJMm",
        "colab": {}
      },
      "source": [
        "len(model.trainable_variables)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "kug5Wg66UJjl",
        "colab": {}
      },
      "source": [
        "model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "f_glpYZ-nYC_"
      },
      "source": [
        "Вы готовы обучать модель.\n",
        "\n",
        "Отметим, что для демонстрационных целей вы запустите только 3 шага на эпоху, но обычно вам нужно указать действительное число шагов, как определено ниже, перед передачей их в `model.fit()`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "AnXPRNWoTypI",
        "colab": {}
      },
      "source": [
        "steps_per_epoch=tf.math.ceil(len(all_image_paths)/BATCH_SIZE).numpy()\n",
        "steps_per_epoch"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "q_8sabaaSGAp",
        "colab": {}
      },
      "source": [
        "model.fit(ds, epochs=1, steps_per_epoch=3)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UMVnoBcG_NlQ"
      },
      "source": [
        "## Производительность\n",
        "\n",
        "Примечание: Этот раздел лишь показывает пару простых приемов которые могут помочь производительности. Для более глубокого изучения см. [Производительность входного конвейера](https://www.tensorflow.org/guide/performance/datasets).\n",
        "\n",
        "Простые конвейеры, использованные выше, прочитывают каждый файл отдельно во время каждой эпохи. Это подходит для локального обучения на CPU, может быть недостаточно для обучения на GPU и абсолютно неприемлемо для любого вида распределенного обучения."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "oNmQqgGhLWie"
      },
      "source": [
        "Чтобы исследовать производительность наших датасетов, сперва постройте простую функцию:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "_gFVe1rp_MYr",
        "colab": {}
      },
      "source": [
        "import time\n",
        "default_timeit_steps = 2*steps_per_epoch+1\n",
        "\n",
        "def timeit(ds, steps=default_timeit_steps):\n",
        "  overall_start = time.time()\n",
        "  # Выберем один пакет для передачи в пайплайн (заполнение буфера перемешивания),\n",
        "  # перед запуском таймера\n",
        "  it = iter(ds.take(steps+1))\n",
        "  next(it)\n",
        "\n",
        "  start = time.time()\n",
        "  for i,(images,labels) in enumerate(it):\n",
        "    if i%10 == 0:\n",
        "      print('.',end='')\n",
        "  print()\n",
        "  end = time.time()\n",
        "\n",
        "  duration = end-start\n",
        "  print(\"{} batches: {} s\".format(steps, duration))\n",
        "  print(\"{:0.5f} Images/s\".format(BATCH_SIZE*steps/duration))\n",
        "  print(\"Total time: {}s\".format(end-overall_start))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "TYiOr4vdLcNX"
      },
      "source": [
        "Производительность данного датасета равна:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "ZDxLwMJOReVe",
        "colab": {}
      },
      "source": [
        "ds = image_label_ds.apply(\n",
        "  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))\n",
        "ds = ds.batch(BATCH_SIZE).prefetch(buffer_size=AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IjouTJadRxyp",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "HsLlXMO7EWBR"
      },
      "source": [
        "### Кеш"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lV1NOn2zE2lR"
      },
      "source": [
        "Используйте tf.data.Dataset.cache, чтобы с легкостью кэшировать вычисления от эпохи к эпохе. Это особенно эффективно если данные помещаются в память.\n",
        "\n",
        "Здесь изображения кэшируются после предварительной обработки (перекодирования и изменения размера):"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "qj_U09xpDvOg",
        "colab": {}
      },
      "source": [
        "ds = image_label_ds.cache()\n",
        "ds = ds.apply(\n",
        "  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))\n",
        "ds = ds.batch(BATCH_SIZE).prefetch(buffer_size=AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "rdxpvQ7VEo3y",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "usIv7MqqZQps"
      },
      "source": [
        "Одним из недостатков использования кэша памяти является то, что кэш должен перестраиваться при каждом запуске, давая одинаковую начальную задержку при каждом запуске датасета:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "eKX6ergKb_xd",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jUzpG4lYNkN-"
      },
      "source": [
        "Если данные не помещаются в памяти, используйте файл кэша:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "vIvF8K4GMq0g",
        "colab": {}
      },
      "source": [
        "ds = image_label_ds.cache(filename='./cache.tf-data')\n",
        "ds = ds.apply(\n",
        "  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))\n",
        "ds = ds.batch(BATCH_SIZE).prefetch(1)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "eTIj6IOmM4yA",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "qqo3dyB0Z4t2"
      },
      "source": [
        "Также у файла кеша есть преимущество использования быстрого перезапуска датасета без перестраивания кеша. Обратите внимание, насколько быстрее это происходит во второй раз:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "hZhVdR8MbaUj",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "WqOVlf8tFrDU"
      },
      "source": [
        "### Файл TFRecord"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "y1llOTwWFzmR"
      },
      "source": [
        "#### Необработанные данные - изображения\n",
        "\n",
        "TFRecord файлы - это простой формат для хранения двоичных блобов (blob). Упаковывая несколько примеров в один файл, TensorFlow может читать несколько элементов за раз, что особенно важно для производительности особенно при использовании удаленного сервиса хранения, такого как GCS.\n",
        "\n",
        "Сперва построим файл TFRecord из необработанных данных изображений:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "EqtARqKuHQLu",
        "colab": {}
      },
      "source": [
        "image_ds = tf.data.Dataset.from_tensor_slices(all_image_paths).map(tf.io.read_file)\n",
        "tfrec = tf.data.experimental.TFRecordWriter('images.tfrec')\n",
        "tfrec.write(image_ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "flR2GXWFKcO1"
      },
      "source": [
        "Затем построим датасет, который прочитывает файл TFRecord и обрабатывает изображения с использованием функции `preprocess_image`, которую вы задали ранее:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "j9PVUL2SFufn",
        "colab": {}
      },
      "source": [
        "image_ds = tf.data.TFRecordDataset('images.tfrec').map(preprocess_image)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "cRp1eZDRKzyN"
      },
      "source": [
        "Объедините этот датасет с датасетом меток, который вы определили ранее, чтобы получить пару из `(image,label)`:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7XI_nDU2KuhS",
        "colab": {}
      },
      "source": [
        "ds = tf.data.Dataset.zip((image_ds, label_ds))\n",
        "ds = ds.apply(\n",
        "  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))\n",
        "ds=ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3ReSapoPK22E",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wb7VyoKNOMms"
      },
      "source": [
        "Это медленнее `cache` версии, поскольку обработанные изображения не кешируются."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "NF9W-CTKkM-f"
      },
      "source": [
        "#### Сериализованные тензоры"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "J9HzljSPkxt0"
      },
      "source": [
        "Чтобы сохранить некоторый препроцессинг в файл TFRecord сперва, как и ранее, создайте датасет обработанных изображений:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "OzS0Azukkjyw",
        "colab": {}
      },
      "source": [
        "paths_ds = tf.data.Dataset.from_tensor_slices(all_image_paths)\n",
        "image_ds = paths_ds.map(load_and_preprocess_image)\n",
        "image_ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "onWOwLpYlzJQ"
      },
      "source": [
        "Сейчас вместо датасета строк `.jpeg`, у вас датасет тензоров.\n",
        "\n",
        "Чтобы сериализовать это в файл TFRecord сперва сконвертируйте датасет тензоров в датасет строк:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "xxZSwnRllyf0",
        "colab": {}
      },
      "source": [
        "ds = image_ds.map(tf.io.serialize_tensor)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "w9N6hJWAkKPC",
        "colab": {}
      },
      "source": [
        "tfrec = tf.data.experimental.TFRecordWriter('images.tfrec')\n",
        "tfrec.write(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "OlFc9dJSmcx0"
      },
      "source": [
        "С кешированным препроцессингом данные могут быть выгружены из TFrecord файла очень эффективно - не забудьте только десериализовать тензор перед использованием:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "BsqFyTBFmSCZ",
        "colab": {}
      },
      "source": [
        "ds = tf.data.TFRecordDataset('images.tfrec')\n",
        "\n",
        "def parse(x):\n",
        "  result = tf.io.parse_tensor(x, out_type=tf.float32)\n",
        "  result = tf.reshape(result, [192, 192, 3])\n",
        "  return result\n",
        "\n",
        "ds = ds.map(parse, num_parallel_calls=AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "OPs_sLV9pQg5"
      },
      "source": [
        "Сейчас добавьте метки и примените те же стандартные операции, что и ранее:"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "XYxBwaLYnGop",
        "colab": {}
      },
      "source": [
        "ds = tf.data.Dataset.zip((ds, label_ds))\n",
        "ds = ds.apply(\n",
        "  tf.data.experimental.shuffle_and_repeat(buffer_size=image_count))\n",
        "ds=ds.batch(BATCH_SIZE).prefetch(AUTOTUNE)\n",
        "ds"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "W8X6RmGan1-P",
        "colab": {}
      },
      "source": [
        "timeit(ds)"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}